package com.yxx.service.Impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.lang.Assert;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yxx.constant.CommonConstant;
import com.yxx.constant.RedisConstant;
import com.yxx.entity.*;
import com.yxx.entityModel.vo.Result.PageResult;
import com.yxx.entityModel.vo.query.ArticleQuery;
import com.yxx.entityModel.vo.query.PageQuery;
import com.yxx.entityModel.vo.request.ArticleRequest;
import com.yxx.entityModel.vo.request.DeleteRequest;
import com.yxx.entityModel.vo.request.RecommendRequest;
import com.yxx.entityModel.vo.request.TopRequest;
import com.yxx.entityModel.vo.response.*;
import com.yxx.enums.ArticleStatusEnum;
import com.yxx.enums.FilePathEnum;
import com.yxx.mapper.*;
import com.yxx.service.ArticleService;
import com.yxx.service.BlogFileService;
import com.yxx.service.RedisService;
import com.yxx.service.TagService;
import com.yxx.strategy.context.SearchStrategyContext;
import com.yxx.strategy.context.UploadStrategyContext;
import com.yxx.utils.BeanCopyUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * 文章模块业务实现类
 * @author yuxiangxun
 * @date 2025/1/21
 * @apiNote
 */
@Service
@Slf4j
public class ArticleServiceImpl  extends ServiceImpl<ArticleMapper, Article> implements ArticleService {
    @Resource
    private ArticleMapper articleMapper;

    @Resource
    private RedisService redisService;

    @Resource
    private CategoryMapper categoryMapper;

    @Resource
    private ArticleTagMapper articleTagMapper;

    @Resource
    private TagService tagService;

    @Resource
    private TagMapper tagMapper;

    @Resource
    private UploadStrategyContext uploadStrategyContext;

    @Resource
    private BlogFileService blogFileService;

    @Resource
    private SearchStrategyContext searchStrategyContext;

    /**
     * 查看后台文章列表
     * @param articleQuery
     * @return
     */
    @Override
    public PageResult<ArticleBackResponse> listAriticleBackVO(ArticleQuery articleQuery) {
        // 查询文章数量
        Long count = articleMapper.selectBackArticleCount(articleQuery);
        // 如果查询出来的文章数量为0，则new一个分页结果对象并返回
        if(count == 0){
            return new PageResult<>();
        }
        // 查询文章后台信息
        List<ArticleBackResponse> articleBackResponseList = articleMapper.selectBackArticleList(articleQuery);
        // 浏览量  Redis Zset
        Map<Object, Double> viewCountMap = redisService.getZsetAllScore(RedisConstant.ARTICLE_VIEW_COUNT);
        // 点赞量  Redis Hash
        Map<String,Integer> likeCountMap = redisService.getHashAll(RedisConstant.ARTICLE_LIKE_COUNT);
        // 封装文章后台信息 还剩下浏览量 点赞量没有封装进去，要封装进去
        articleBackResponseList.forEach(item -> {
            item.setViewCount(Optional.ofNullable(viewCountMap.get(item.getId())).orElse((double) 0).intValue());
            item.setLikeCount(Optional.ofNullable(likeCountMap.get(item.getId().toString())).orElse(0).intValue());
        });
        return new PageResult<>(articleBackResponseList,count);
    }

    /**
     * 添加文章
     * @param articleRequest
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addArticle(ArticleRequest articleRequest) {
        // 因为文章是有分类的，所以先保存文章分类数据，返回保存分类的id
        Integer categoryId = saveArticleCategory(articleRequest);
        // 添加文章
        Article newArticle = BeanCopyUtils.copyBean(articleRequest, Article.class);
        if(StringUtils.isBlank(newArticle.getArticleCover())){
            SiteConfig siteConfig = redisService.getObject(RedisConstant.SITE_SETTING);
            newArticle.setArticleContent(siteConfig.getArticleCover());
        }
        newArticle.setCategoryId(categoryId);
        newArticle.setUserId(StpUtil.getLoginIdAsInt());
        baseMapper.insert(newArticle);
        // 因为文章有标签，所以保存文章标签
        saveArticleTag(articleRequest,newArticle.getId());
    }



    /**
     * 保存文章分类数据，返回保存分类的id
     * @param articleRequest
     * @return
     */
    private Integer saveArticleCategory(ArticleRequest articleRequest) {
        // 先查询这个分类是否存在
        Category existCategory = categoryMapper.selectOne(new LambdaQueryWrapper<Category>()
                .select(Category::getId)
                .eq(Category::getCategoryName, articleRequest.getCategoryName())
        );
        // 如果分类不存在，新增一个分类，返回这个分类id
        if(Objects.isNull(existCategory)){
            existCategory = Category.builder()
                    .categoryName(articleRequest.getCategoryName())
                    .build();
            // 保存分类
            categoryMapper.insert(existCategory);
        }
        // 分类存在，直接分类id
        return existCategory.getId();
    }

    /**
     * 保存文章标签数据
     * 保存新增文章的标签数据
     * 保存修改文章的标签数据
     * @param articleRequest
     * @param articleId
     */
    private void saveArticleTag(ArticleRequest articleRequest, Integer articleId) {
        // 删除文章标签数据 根据文章id删除文章标签数据
        articleTagMapper.delete(new LambdaQueryWrapper<ArticleTag>()
                .eq(ArticleTag::getArticleId,articleId));
        // 标签名列表(请求中的标签名称列表)
        List<String> tagNameList = articleRequest.getTagNameList();
        // 如果请求中的标签列表为空，表示本文章没有标签，直接返回即可，无需增加标签操作
        if(CollectionUtils.isEmpty(tagNameList)){
            return;
        }


        // 数据库中查询出已存在的标签  这里是根据请求中的标签名称列表查询出数据库中对应存在的标签
        List<Tag> existTagList = tagMapper.selectList(new LambdaQueryWrapper<Tag>()
                        .select(Tag::getId,Tag::getTagName)
                        .in(Tag::getTagName, tagNameList));
        // 这里做处理，将数据库中存在的标签名称列表提取出来，并封装成id列表
        List<String> existTagNameList = existTagList.stream()
                .map(existTag -> existTag.getTagName())
                .collect(Collectors.toList());
        // 这里做处理，将数据库中存在的标签名称列表提取
        List<Integer> existTagIdList = existTagList.stream()
                .map(existTag -> existTag.getId())
                .collect(Collectors.toList());
        // 移除请求中的标签名称列表中 已存在于 数据库中标签名称 的标签列表（即为数据库中没有的标签---新标签）
        tagNameList.removeAll(existTagNameList);


        // 判断是否有新标签
        if(CollectionUtils.isNotEmpty(tagNameList)){
             // 有新标签，构造新标签对象列表
             List<Tag> newTagList = tagNameList.stream().map(newTagName -> Tag.builder()
                     .tagName(newTagName)
                     .build()).collect(Collectors.toList());
             // 批量保存新标签
             tagService.saveBatch(newTagList);
             // 获取新标签id列表
            List<Integer> newTagIdList = newTagList.stream()
                    .map(newTag -> newTag.getId())
                    .collect(Collectors.toList());
            // 将存入到数据库中的新生成的标签id添加到id列表中
            existTagIdList.addAll(newTagIdList);
        }


        // 将所有的标签绑定到文章标签关联表
        articleTagMapper.saveBatchArticleTag(articleId,existTagIdList);
    }


    /**
     * 删除文章
     * @param articleIdList
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteArticle(List<Integer> articleIdList) {
        // 删除对应的文章标签数据  修改bug: 修复无法删除文章标签的问题
        articleTagMapper.delete(new LambdaQueryWrapper<ArticleTag>()
                .in(ArticleTag::getArticleId, articleIdList));
        // 批量删除删除文章
        articleMapper.deleteBatchIds(articleIdList);
    }

    /**
     * 回收或恢复文章
     * @param deleteRequest
     */
    @Override
    public void updateArticleDelete(DeleteRequest deleteRequest) {
        // 批量更新文章 逻辑删除 的状态
        List<Article> articleList = deleteRequest.getIdList().stream()
                .map(id -> Article.builder()
                        .id(id)
                        .isDelete(deleteRequest.getIsDelete())
                        .isTop(CommonConstant.FALSE)
                        .isRecommend(CommonConstant.FALSE)
                        .build())
                .collect(Collectors.toList());
        this.updateBatchById(articleList);
    }

    /**
     * 修改文章
     * @param articleRequest
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateArticle(ArticleRequest articleRequest) {
        // 保存文章分类
        Integer categoryId = saveArticleCategory(articleRequest);
        // 对象转换，修改文章对象
        Article newArticle = BeanCopyUtils.copyBean(articleRequest, Article.class);
        newArticle.setCategoryId(categoryId);
        newArticle.setUserId(StpUtil.getLoginIdAsInt());
        baseMapper.updateById(newArticle);
        // 保存文章标签
        saveArticleTag(articleRequest, newArticle.getId());
    }

    /**
     * 编辑文章
     * @param articleId
     * @return
     */
    @Override
    public ArticleInfoResponse editArticle(Integer articleId) {
        // 查询文章信息
        ArticleInfoResponse articleInfoResponse = articleMapper.selectArticleInfoById(articleId);
        // 断言，如果文章不存在，抛出异常
        Assert.notNull(articleInfoResponse, "文章不存在");
        // 查询文章分类名称
        Category category = categoryMapper.selectOne(new LambdaQueryWrapper<Category>()
                .select(Category::getCategoryName)
                .eq(Category::getId, articleInfoResponse.getCategoryId())
        );
        // 查询文章标签列表信息信息
        List<String> tagNameList = tagMapper.selectTagNameByArticleId(articleId);
        articleInfoResponse.setCategoryName(category.getCategoryName());
        articleInfoResponse.setTagNameList(tagNameList);
        return articleInfoResponse;
    }

    /**
     * 上传文章图片
     * @param file
     * @return
     */
    @Override
    public String saveArticleImages(MultipartFile file) {
        // 上传文件
        String url = uploadStrategyContext.executeUploadStrategy(file, FilePathEnum.ARTICLE.getPath());
        blogFileService.saveBlogFile(file, url, FilePathEnum.ARTICLE.getFilePath());
        return url;
    }


    /**
     * 修改文章置顶状态
     * @param topRequest
     */
    @Override
    public void updateArticleTop(TopRequest topRequest) {
        // 修改文章置顶状态
        Article newArticle = Article.builder()
                .id(topRequest.getId())
                .isTop(topRequest.getIsTop())
                .build();
        articleMapper.updateById(newArticle);
    }

    /**
     *  推荐文章
     * @param recommendRequest
     */
    @Override
    public void updateArticleRecommend(RecommendRequest recommendRequest) {
        // 修改文章推荐状态
        Article newArticle = Article.builder()
                .id(recommendRequest.getId())
                .isRecommend(recommendRequest.getIsRecommend())
                .build();
        articleMapper.updateById(newArticle);
    }

    /**
     * 文章搜索
     * @param keyword
     * @return
     */
    @Override
    public List<ArticleSearchResponse> listArticlesBySearch(String keyword) {
        return searchStrategyContext.executeSearchStrategy(keyword);
    }

    @Override
    public PageResult<ArticleHomeResponse> listArticleHomeVO(PageQuery pageQuery) {
        // 查询文章数量
        Long count = articleMapper.selectCount(new LambdaQueryWrapper<Article>()
                .eq(Article::getIsDelete, CommonConstant.FALSE)
                .eq(Article::getStatus, ArticleStatusEnum.PUBLIC.getStatus()));
        if (count == 0) {
            return new PageResult<>();
        }
        // 查询首页文章  // fix:修改查询首页文章的sql语句，没有查询id导致查看文章出现前端传递了无法转换为整数的字符串 "NaN" 到后端接口
        List<ArticleHomeResponse> articleHomeVOList = articleMapper.selectArticleHomeList(pageQuery);
        return new PageResult<>(articleHomeVOList, count);
    }

    /**
     * 查看文章
     * @param articleId
     * @return
     */
    @Override
    public ArticleResponse getArticleHomeById(Integer articleId) {
        // 查询文章信息
        ArticleResponse articleResponse = articleMapper.selectArticleHomeById(articleId);
        if(Objects.isNull(articleResponse)){
            return null;
        }
        // 查询点赞量
        Integer likeCount = redisService.getHash(RedisConstant.ARTICLE_LIKE_COUNT, articleId.toString());
        articleResponse.setLikeCount(Optional.ofNullable(likeCount).orElse(0));
        // 查询上一章
        ArticlePaginationResponse lastArticle = articleMapper.selectLastArticle(articleId);
        articleResponse.setLastArticle(lastArticle);
        // 查询下一章
        ArticlePaginationResponse nextArticle = articleMapper.selectNextArticle(articleId);
        articleResponse.setNextArticle(nextArticle);
        // 先浏览量+1
        redisService.incrZet(RedisConstant.ARTICLE_VIEW_COUNT,articleId, 10.0);
        // 查询浏览量
        Double viewCount = Optional.ofNullable(redisService.getZsetScore(RedisConstant.ARTICLE_VIEW_COUNT, articleId)).orElse((double) 0);
        articleResponse.setViewCount(viewCount.intValue());
        return articleResponse;
    }

    /**
     * 推荐文章
     * @return
     */
    @Override
    public List<ArticleRecommendResponse> listArticleRecommendVO() {
        return articleMapper.selectArticleRecommend();
    }

    /**
     * 查看文章归档
     * @param pageQuery
     * @return
     */
    @Override
    public PageResult<ArchiveResponse> listArchiveVO(PageQuery pageQuery) {
        // 查询归档文章数量
        Long count = articleMapper.selectCount(new LambdaQueryWrapper<Article>()
                .eq(Article::getIsDelete, CommonConstant.FALSE)
                .eq(Article::getStatus, ArticleStatusEnum.PUBLIC.getStatus())
        );
        // 如果归档文章数量为0，返回空分页结果
        if(count == 0){
            return new PageResult<>();
        }
        // 如果归档文章数量不为0，分页查询归档文章列表并返回
        List<ArchiveResponse> archiveResponses = articleMapper.selectArchiveList(pageQuery);
        return new PageResult<>(archiveResponses, count);
    }
}
